/*****************************************************************************
 *   Copyright (C) 2008 John Schember <john@nachtimwald.com>                 *
 *                                                                           *
 *   This file is part of niwbillmanager.                                    *
 *                                                                           *
 *   niwbillmanager is free software: you can redistribute it and/or         *
 *   modify it under the terms of the GNU General Public License as          *
 *   published by the Free Software Foundation, either version 3 of the      *
 *   License, or (at your option) any later version.                         *
 *                                                                           *
 *   niwbillmanager is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
 *   GNU General Public License for more details.                            *
 *                                                                           *
 *   You should have received a copy of the GNU General Public License       *
 *   along with niwbillmanager. If not, see                                  *
 *   <http://www.gnu.org/licenses/>.                                         *
 *****************************************************************************/

#include "datastore.h"

#include <QSqlError>
#include <QUuid>
#include <QVariant>

#include <transaction.h>

QString UndoRedoType::undoRedoTypeToString(UndoRedoType::UndoRedoTypes
    undoRedoType)
{
    QString undoRedoString = "";

    switch (undoRedoType)
    {
        case UndoRedoType::UNDO:
            undoRedoString = "undo";
            break;
        case UndoRedoType::REDO:
            undoRedoString = "redo";
            break;
        default:
            break;
    }

    return undoRedoString;
}

QString QueryType::queryTypeToString(QueryType::QueryTypes queryType)
{
    QString queryTypeString = "";

    switch (queryType)
    {
        case QueryType::AND:
            queryTypeString = "AND";
            break;
        case QueryType::OR:
            queryTypeString = "OR";
            break;
        default:
            break;
    }

    return queryTypeString;
}

DataStore::DataStore()
{
    m_dbName = "libniwbillmanager-" + QUuid::createUuid().toString();
    m_db = QSqlDatabase::addDatabase("QSQLITE", m_dbName);
    m_db.setDatabaseName(":memory:");

    m_db.open();

    m_query = new QSqlQuery(m_db);

    // Create the tables we are storing data in.
    m_query->exec("CREATE TABLE collection \
        (id integer primary key, amountdue TEXT,  autopay TEXT, \
        datedue TEXT, payee TEXT, paymentlocation TEXT, paymentmethod TEXT, \
        paymentrecurringperiod TEXT, paid TEXT, name TEXT, notes TEXT, \
        tags TEXT);");
    m_query->exec("CREATE TABLE transactionlog \
        (transactiontype TEXT, id TEXT);");
    m_query->exec("CREATE TABLE undo \
        (transactiontype TEXT, id TEXT, amountdue TEXT,  autopay TEXT, \
        datedue TEXT, payee TEXT, paymentlocation TEXT, paymentmethod TEXT, \
        paymentrecurringperiod TEXT, paid TEXT, name TEXT, notes TEXT, \
        tags TEXT);");
    m_query->exec("CREATE TABLE redo \
        (transactiontype TEXT, id TEXT, amountdue TEXT,  autopay TEXT, \
        datedue TEXT, payee TEXT, paymentlocation TEXT, paymentmethod TEXT, \
        paymentrecurringperiod TEXT, paid TEXT, name TEXT, notes TEXT, \
        tags TEXT);");
}

DataStore::~DataStore()
{
    delete m_query;
    m_query = 0;

    m_db.close();
    m_db = QSqlDatabase();
    QSqlDatabase::removeDatabase(m_dbName);
}

void DataStore::clearCollection()
{
    m_query->exec("DELETE FROM collection;");
    clearRawTransactionLog();
    clearUndoRedoBuffer(UndoRedoType::UNDO);
    clearUndoRedoBuffer(UndoRedoType::REDO);
}

QString DataStore::addBillItem(BillItem billItem, bool opening)
{
    // Try to insert into the collection
    if (queryInsertBillItem(billItem))
    {
        QString insertId = m_query->lastInsertId().toString();

        // If this is part of opening a collection we don't want to add an
        // undo or a transaction for this add.
        if (!opening) {
            // Insert the undo command.
            billItem.setId(insertId);
            addUndoRedoItem(UndoRedoType::UNDO, TransactionType::DELETE,
                billItem);

            clearUndoRedoBuffer(UndoRedoType::REDO);

            // Update the transaction log.
            addTransactionItem(TransactionType::INSERT, insertId);
        }

        return insertId;
    }

    return QString();
}

bool DataStore::modifyBillItem(BillItem billItem)
{
    BillItem oldBillItem;

    // Store the old item data for use in the undo buffer.
    if (!getBillItemById(billItem.getId(), oldBillItem)) {
        return false;
    }

    if (!queryUpdateBillItem(billItem)) {
        return false;
    }

    // Put the old bill item into undo.
    addUndoRedoItem(UndoRedoType::UNDO, TransactionType::UPDATE, oldBillItem);
    clearUndoRedoBuffer(UndoRedoType::REDO);

    // Update the transaction log.
    addTransactionItem(TransactionType::UPDATE, billItem.getId());

    return true;
}

bool DataStore::deleteBillItem(QString billId)
{
    BillItem billItem;

    if (getBillItemById(billId, billItem)) {
        // Put the bill item into undo.
        addUndoRedoItem(UndoRedoType::UNDO, TransactionType::INSERT, billItem);
        clearUndoRedoBuffer(UndoRedoType::REDO);

        // Delete the item from collection.
        queryDeleteBillItem(billId);

        // Update the transaction log.
        addTransactionItem(TransactionType::DELETE, billItem.getId());

        return true;
    }

    return false;
}

bool DataStore::getBillItemById(QString billId, BillItem &billItem)
{
    // Make sure the query is valied. Is the bill id valid.
    if (m_query->exec("SELECT id, amountdue, autopay, datedue, payee, \
        paymentlocation, paymentmethod, paymentrecurringperiod, paid, name, \
        notes, tags FROM collection WHERE id='" + billId + "';"))
    {
        return getNextBillItem(billItem);
    }

    return true;
}

void DataStore::startGetBillItemsFromTags(QSet<QString> tags)
{
    QString sqlString;

    if (!tags.isEmpty()) {
        sqlString = " ( ";

        Q_FOREACH (QString item, tags) {
            sqlString += " OR tags LIKE '% " + item + " %' ";
        }
        sqlString.remove(4, 3);

        if (!sqlString.isEmpty()) {
            sqlString.prepend(" WHERE ");
        }
    }
    else {
        sqlString = " WHERE tags LIKE '%  %'";
    }

    m_query->exec("SELECT id, amountdue, autopay, datedue, payee, \
        paymentlocation, paymentmethod, paymentrecurringperiod, paid, name, \
        notes, tags FROM collection " + sqlString + ";");
}

void DataStore::startGetBillItemsWithoutTags()
{
    QSet<QString> tags;
    QString tag = "";

    tags.insert(tag);

    startGetBillItemsFromTags(tags);
}

void DataStore::startGetAllBillItems()
{
    m_query->exec("SELECT id, amountdue, autopay, datedue, payee, \
        paymentlocation, paymentmethod, paymentrecurringperiod, paid, name, \
        notes, tags FROM collection");
}

void DataStore::startGetBillItems(QSet<QString> query,
    QueryType::QueryTypes queryType,
    bool amountDue, bool autoPay, bool nonAutoPay,
    bool dateDue, bool payee, bool paymentLocation,
    bool paymentMethod, bool paymentRecurringPeriod,
    bool recurringOnly, bool nonRecurring, bool paid, bool unpaid,
    bool name, bool notes, bool tags)
{
    QSet<QString> itemizedQuery;
    QString sqlString = "";
    QString searchString = "";
    QString queryTypeString = QueryType::queryTypeToString(queryType);
    queryTypeString = " " + queryTypeString + " ";

    Q_FOREACH (QString item, query) {
        item.prepend("'%");
        item.append("%'");
        itemizedQuery.insert(item);
    }

    if (amountDue) {
        searchString = "";
        sqlString += queryTypeString + "( ";
        Q_FOREACH (QString item, itemizedQuery) {
            searchString += " OR amountdue LIKE " + item + " ";
        }
        searchString.remove(0, 4);
        sqlString += searchString + " ) ";
    }
    if (dateDue) {
        searchString = "";
        sqlString += queryTypeString + "( ";
        Q_FOREACH (QString item, itemizedQuery) {
            searchString += " OR datedue LIKE " + item + " ";
        }
        searchString.remove(0, 4);
        sqlString += searchString + " ) ";
    }
    if (payee) {
        searchString = "";
        sqlString += queryTypeString + "( ";
        Q_FOREACH (QString item, itemizedQuery) {
            searchString += " OR payee LIKE " + item + " ";
        }
        searchString.remove(0, 4);
        sqlString += searchString + " ) ";
    }
    if (paymentLocation) {
        searchString = "";
        sqlString += queryTypeString + "( ";
        Q_FOREACH (QString item, itemizedQuery) {
            searchString += " OR paymentlocation LIKE " + item + " ";
        }
        searchString.remove(0, 4);
        sqlString += searchString + " ) ";
    }
    if (paymentMethod) {
        searchString = "";
        sqlString += queryTypeString + "( ";
        Q_FOREACH (QString item, itemizedQuery) {
            searchString += " OR paymentmethod LIKE " + item + " ";
        }
        searchString.remove(0, 4);
        sqlString += searchString + " ) ";
    }
    if (paymentRecurringPeriod) {
        searchString = "";
        sqlString += queryTypeString + "( ";
        Q_FOREACH (QString item, itemizedQuery) {
            searchString += " OR paymentrecurringperiod LIKE " + item + " ";
        }
        searchString.remove(0, 4);
        sqlString += searchString + " ) ";
    }
    if (name) {
        searchString = "";
        sqlString += queryTypeString + "( ";
        Q_FOREACH (QString item, itemizedQuery) {
            searchString += " OR name LIKE " + item + " ";
        }
        searchString.remove(0, 4);
        sqlString += searchString + " ) ";
    }
    if (notes) {
        searchString = "";
        sqlString += queryTypeString + "( ";
        Q_FOREACH (QString item, itemizedQuery) {
            searchString += " OR notes LIKE " + item + " ";
        }
        searchString.remove(0, 4);
        sqlString += searchString + " ) ";
    }
    if (tags) {
        searchString = "";
        sqlString += queryTypeString + "( ";
        Q_FOREACH (QString item, query) {
            searchString += " OR tags LIKE '% " + item + " %' ";
        }
        searchString.remove(0, 4);
        sqlString += searchString + " ) ";
    }

    if (autoPay && !nonAutoPay) {
        sqlString += " AND autopay = 'T' ";
    }
    else if (nonAutoPay && !autoPay) {
        sqlString += " AND autopay = 'F' ";
    }

    if (recurringOnly && !nonRecurring) {
        sqlString += " AND paymentrecurringperiod NOT LIKE 'none' ";
    }
    else if(nonRecurring && !recurringOnly) {
        sqlString += " AND paymentrecurringperiod LIKE 'none' ";
    }

    if (paid && !unpaid) {
        sqlString =+ " AND paid = 'T' ";
    }
    else if (unpaid && !paid) {
        sqlString += " AND paid = 'F' ";
    }

    if (sqlString.startsWith(" OR ")) {
        sqlString.remove(0, 4);
    }
    else if(sqlString.startsWith(" AND ")) {
        sqlString.remove(0, 5);
    }

    if (!sqlString.isEmpty()) {
        sqlString.prepend(" WHERE ");
    }

    m_query->exec("SELECT id, amountdue, autopay, datedue, payee, \
        paymentlocation, paymentmethod, paymentrecurringperiod, paid, name, \
        notes, tags FROM collection " + sqlString + ";");
}

bool DataStore::hasMoreBillItems()
{
    return queryHasMore("collection");
}

bool DataStore::getNextBillItem(BillItem &billItem)
{
    // This prevents us from getting non-existant data.
    if (!hasMoreBillItems()) {
        return false;
    }

    // Grab the next item.
    if (!m_query->next()) {
        return false;
    }

    if (!m_query->value(0).toString().isEmpty()) {
        billItem.setId(m_query->value(0).toString());
    }
    if (!m_query->value(1).toString().isEmpty()) {
        billItem.setAmountDue(m_query->value(1).toString());
    }
    if (!m_query->value(2).toString().isEmpty()) {
        billItem.setAutoPay(m_query->value(2).toString()=="T"?true:false);
    }
    if (!m_query->value(3).toString().isEmpty()) {
        billItem.setDateDue(QDate::fromString(
            m_query->value(3).toString(), Qt::ISODate));
    }
    if (!m_query->value(4).toString().isEmpty()) {
        billItem.setPayee(m_query->value(4).toString());
    }
    if (!m_query->value(5).toString().isEmpty()) {
        billItem.setPaymentLocation(m_query->value(5).toString());
    }
    if (!m_query->value(6).toString().isEmpty()) {
        billItem.setPaymentMethod(m_query->value(6).toString());
    }
    if (!m_query->value(7).toString().isEmpty()) {
        billItem.setPaymentRecurringPeriod(
            RecurringPeriodType::stringToRecurringPeriodType(
            m_query->value(7).toString()));
    }
    if (!m_query->value(8).toString().isEmpty()) {
        billItem.setPaid(m_query->value(8).toString()=="T"?true:false);
    }
    if (!m_query->value(9).toString().isEmpty()) {
        billItem.setName(m_query->value(9).toString());
    }
    if (!m_query->value(10).toString().isEmpty()) {
        billItem.setNotes(m_query->value(10).toString());
    }
    if (!m_query->value(11).toString().isEmpty()) {
        billItem.setTags(m_query->value(11).toString().split(" ").toSet());
    }

    return true;
}

QSet<QString> DataStore::getAllTags()
{
    QSet<QString> tags;

    m_query->exec("SELECT tags FROM collection;");

    while (m_query->next()) {
        Q_FOREACH (QString tag, m_query->value(0).toString().split(" ")) {
            tags.insert(tag);
        }
    }

    return tags;
}

QSet<QString> DataStore::getTags(QString billId)
{
    m_query->exec("SELECT tags FROM collection WHERE id='" + billId + "';");

    m_query->next();

    return m_query->value(0).toString().split(" ").toSet();
}

bool DataStore::canUndoChange()
{
    return canUndoRedoChange(UndoRedoType::UNDO);
}

bool DataStore::canRedoChange()
{
    return canUndoRedoChange(UndoRedoType::REDO);
}

bool DataStore::undoChange(Transaction &transaction)
{
    return undoRedoChange(UndoRedoType::UNDO, transaction);
}

bool DataStore::redoChange(Transaction &transaction)
{
    return undoRedoChange(UndoRedoType::REDO, transaction);
}

void DataStore::clearRawTransactionLog()
{
    m_query->exec("DELETE FROM transactionlog;");
}

void DataStore::startGetRawTransactionLog()
{
    m_query->exec("SELECT transactiontype, id FROM transactionlog");
}

bool DataStore::hasMoreRawTransactions()
{
    return queryHasMore("transactionlog");
}

ShortTransaction DataStore::getNextRawTransaction()
{
    ShortTransaction transaction;

    m_query->next();

    transaction.setTransactionType(TransactionType::stringToTransactionType(
        m_query->value(0).toString()));
    transaction.setBillId(m_query->value(1).toString());

    return transaction;
}

bool DataStore::queryHasMore(const QString &table)
{
    // Check to see if the last query executed was the select all query.
    // We get the items from this query going though the db with query->next().
    if (!m_query->lastQuery().contains("FROM " + table)) {
        return false;
    }

    // Query must be active for m_query->next() to work.
    if (!m_query->isActive()) {
        return false;
    }

    // Check to see if the next query is avaliable.
    if (!m_query->next()) {
        return false;
    }

    // Non-valid records cannt be retrieved.
    if (!m_query->isValid()) {
        return false;
    }

    // Place the recored back to where it started so readNextBillItem can
    // get the record. QSqlQuery has no peek function. We don't care if the
    // last query if valied because we will only be retrieving the next one.
    m_query->previous();

    return true;
}

bool DataStore::queryInsertBillItem(const BillItem &billItem)
{
    return m_query->exec("INSERT INTO collection (amountdue, \
        autopay, datedue, payee, paymentlocation, paymentmethod, \
        paymentrecurringperiod, paid, name, notes, tags) values ("
        "'" + billItem.getAmountDue() + "', "
        "'" + (billItem.getAutoPay()?"T":"F") + "', "
        "'" + billItem.getDateDue().toString(Qt::ISODate) + "', "
        "'" + billItem.getPayee() + "', "
        "'" + billItem.getPaymentLocation() + "', "
        "'" + billItem.getPaymentMethod() + "', "
        "'" + RecurringPeriodType::recurringPeriodTypeToString(
            billItem.getPaymentRecurringPeriod()) + "', "
        "'" + (billItem.getPaid()?"T":"F") + "', "
        "'" + billItem.getName() + "', "
        "'" + billItem.getNotes() + "', "
        "' " + BillItem::stringSetToString(billItem.getTags()) + " '"
        ");");
}

bool DataStore::queryInsertBillItemWithId(const BillItem &billItem)
{
    return m_query->exec("INSERT INTO collection (id, amountdue, \
        autopay, datedue, payee, paymentlocation, paymentmethod, \
        paymentrecurringperiod, paid, name, notes, tags) values ("
        "'" + billItem.getId() + "', "
        "'" + billItem.getAmountDue() + "', "
        "'" + (billItem.getAutoPay()?"T":"F") + "', "
        "'" + billItem.getDateDue().toString(Qt::ISODate) + "', "
        "'" + billItem.getPayee() + "', "
        "'" + billItem.getPaymentLocation() + "', "
        "'" + billItem.getPaymentMethod() + "', "
        "'" + RecurringPeriodType::recurringPeriodTypeToString(
            billItem.getPaymentRecurringPeriod()) + "', "
        "'" + (billItem.getPaid()?"T":"F") + "', "
        "'" + billItem.getName() + "', "
        "'" + billItem.getNotes() + "', "
        "' " + BillItem::stringSetToString(billItem.getTags()) + " '"
        ");");
}

bool DataStore::queryUpdateBillItem(const BillItem &billItem)
{
    // Update the bill item.
    return m_query->exec("UPDATE collection SET "
        "amountdue='" + billItem.getAmountDue() + "', "
        "autopay='" + (billItem.getAutoPay()?"T":"F") + "', "
        "datedue='" + billItem.getDateDue().toString(Qt::ISODate) + "', "
        "payee='" + billItem.getPayee() + "', "
        "paymentlocation='" + billItem.getPaymentLocation() + "', "
        "paymentmethod='" + billItem.getPaymentMethod() + "', "
        "paymentrecurringperiod='" +
            RecurringPeriodType::recurringPeriodTypeToString(
            billItem.getPaymentRecurringPeriod()) + "', "
        "paid='" + (billItem.getPaid()?"T":"F") + "', "
        "name='" + billItem.getName() + "', "
        "notes='" + billItem.getNotes() + "', "
        "tags=' " + BillItem::stringSetToString(billItem.getTags()) + " ' "
        "WHERE id='" + billItem.getId() + "'"
        ";");
}

bool DataStore::queryDeleteBillItem(const QString &billId)
{
    return m_query->exec("DELETE FROM collection WHERE id='" + billId +
        "';");
}

void DataStore::addTransactionItem(
    TransactionType::TransactionTypes transactionType, const QString &billId)
{
    m_query->exec("INSERT INTO transactionlog (transactiontype, id) \
        values ('"
        + TransactionType::transactionTypeToString(transactionType)
        + "', '" + billId + "');");
}

void DataStore::addUndoRedoItem(UndoRedoType::UndoRedoTypes undoRedoType,
    TransactionType::TransactionTypes transactionType, BillItem billItem)
{
    m_query->exec("INSERT INTO "
        + UndoRedoType::undoRedoTypeToString(undoRedoType) +
        " (transactiontype, id, amountdue, autopay, datedue, payee, \
        paymentlocation, paymentmethod, paymentrecurringperiod, paid, name, \
        notes, tags) values ("
        "'" + TransactionType::transactionTypeToString(transactionType) + "', "
        "'" + billItem.getId() + "', "
        "'" + billItem.getAmountDue() + "', "
        "'" + (billItem.getAutoPay()?"T":"F") + "', "
        "'" + billItem.getDateDue().toString(Qt::ISODate) + "', "
        "'" + billItem.getPayee() + "', "
        "'" + billItem.getPaymentLocation() + "', "
        "'" + billItem.getPaymentMethod() + "', "
        "'" + RecurringPeriodType::recurringPeriodTypeToString(
            billItem.getPaymentRecurringPeriod()) + "', "
        "'" + (billItem.getPaid()?"T":"F") + "', "
        "'" + billItem.getName() + "', "
        "'" + billItem.getNotes() + "', "
        "'" + BillItem::stringSetToString(billItem.getTags()) + "'"
        ");");
}

void DataStore::removeUndoRedoItem(UndoRedoType::UndoRedoTypes undoRedoType,
    QString rowId)
{
    m_query->exec("DELETE FROM "
        + UndoRedoType::undoRedoTypeToString(undoRedoType) +
        " WHERE ROWID='" + rowId + "';");
}

bool DataStore::canUndoRedoChange(UndoRedoType::UndoRedoTypes undoRedoType)
{
    m_query->exec("SELECT id FROM " +
        UndoRedoType::undoRedoTypeToString(undoRedoType) + ";");

    if (m_query->next()) {
        return true;
    }

    return false;
}

bool DataStore::undoRedoChange(const UndoRedoType::UndoRedoTypes &undoRedoType,
    Transaction &transaction)
{
    if (!canUndoRedoChange(undoRedoType)) {
        return false;
    }

    UndoRedoType::UndoRedoTypes oppositeUndoRedoType;

    switch (undoRedoType)
    {
        case UndoRedoType::UNDO:
            oppositeUndoRedoType = UndoRedoType::REDO;
            break;
        case UndoRedoType::REDO:
            oppositeUndoRedoType = UndoRedoType::UNDO;
            break;
        default:
            return false;
            break;
    }

    BillItem billItem;
    QString rowId;

    getLastUndoRedoItem(undoRedoType, transaction, rowId);

    switch (transaction.getTransactionType())
    {
        case TransactionType::INSERT:
            billItem.setId(transaction.getBillItem().getId());
            if (!queryInsertBillItemWithId(transaction.getBillItem())) {
                return false;
            }
            addUndoRedoItem(oppositeUndoRedoType, TransactionType::DELETE,
                billItem);
            break;
        case TransactionType::UPDATE:
            getBillItemById(transaction.getBillItem().getId(), billItem);
            if (!queryUpdateBillItem(transaction.getBillItem())) {
                return false;
            }
            addUndoRedoItem(oppositeUndoRedoType, TransactionType::UPDATE,
                billItem);
            break;
        case TransactionType::DELETE:
            getBillItemById(transaction.getBillItem().getId(), billItem);
            if (!queryDeleteBillItem(transaction.getBillItem().getId())) {
                return false;
            }
            addUndoRedoItem(oppositeUndoRedoType, TransactionType::INSERT,
                billItem);
            break;
        default:
            return false;
            break;
    }

    // Remove the item from the buffer.
    removeUndoRedoItem(undoRedoType, rowId);

    return true;
}

bool DataStore::getLastUndoRedoItem(UndoRedoType::UndoRedoTypes undoRedoType,
    Transaction &transaction, QString &rowId)
{
    // Get the last bill item from the buffer.
    m_query->exec("SELECT transactiontype, id, amountdue,  autopay, \
        datedue, payee, paymentlocation, paymentmethod, \
        paymentrecurringperiod, paid, name, notes, tags, ROWID FROM " +
        UndoRedoType::undoRedoTypeToString(undoRedoType) + ";");

    if (!m_query->last()) {
        return false;
    }

    BillItem billItem;
    QString transactionString = m_query->value(0).toString();

    // Make sure there is a Transaction Type.
    if (transactionString.isEmpty()) {
        return false;
    }

    // Load all the data from the last bill item into a bill item object.
    if (!m_query->value(1).toString().isEmpty()) {
        billItem.setId(m_query->value(1).toString());
    }
    else {
        return false;
    }
    if (!m_query->value(2).toString().isEmpty()) {
        billItem.setAmountDue(m_query->value(2).toString());
    }
    if (!m_query->value(3).toString().isEmpty()) {
        billItem.setAutoPay(m_query->value(3).toString()=="T"?true:false);
    }
    if (!m_query->value(4).toString().isEmpty()) {
        billItem.setDateDue(QDate::fromString(
            m_query->value(4).toString(), Qt::ISODate));
    }
    if (!m_query->value(5).toString().isEmpty()) {
        billItem.setPayee(m_query->value(5).toString());
    }
    if (!m_query->value(6).toString().isEmpty()) {
        billItem.setPaymentLocation(m_query->value(6).toString());
    }
    if (!m_query->value(7).toString().isEmpty()) {
        billItem.setPaymentMethod(m_query->value(7).toString());
    }
    if (!m_query->value(8).toString().isEmpty()) {
        billItem.setPaymentRecurringPeriod(
            RecurringPeriodType::stringToRecurringPeriodType(
            m_query->value(8).toString()));
    }
    if (!m_query->value(9).toString().isEmpty()) {
        billItem.setPaid(m_query->value(9).toString()=="T"?true:false);
    }
    if (!m_query->value(10).toString().isEmpty()) {
        billItem.setName(m_query->value(10).toString());
    }
    if (!m_query->value(11).toString().isEmpty()) {
        billItem.setNotes(m_query->value(11).toString());
    }
    if (!m_query->value(12).toString().isEmpty()) {
        billItem.setTags(m_query->value(12).toString().split(" ").toSet());
    }

    // Get the row id for this item so it can be removed later.
    if (m_query->value(13).toString().isEmpty()) {
        return false;
    }
    rowId = m_query->value(13).toString();

    // Set the transaction type.
    transaction.setTransactionType(TransactionType::stringToTransactionType(
        transactionString));
    // Save the bill item object in the transaction object.
    transaction.setBillItem(billItem);

    return true;
}

void DataStore::clearUndoRedoBuffer(UndoRedoType::UndoRedoTypes undoRedoType)
{
    m_query->exec("DELETE FROM "
        + UndoRedoType::undoRedoTypeToString(undoRedoType) + ";");
}
